
#include <BPainter>
#include <BKeyEvent>
#include <BMouseEvent>

#include <BScrollBar>
#include "member_BScrollBar.h"
#include "member_BWidget.h"

using namespace BWE;

#define member					(*(member_BScrollBar*)_ptr)
#define member_allocate()		_ptr = new member_BScrollBar(this)
#define member_release()		delete (member_BScrollBar*)_ptr

BScrollBar::BScrollBar(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BScrollBar::BScrollBar(Orientation orientation, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	member.orientation = orientation;
}
BScrollBar::~BScrollBar()
{
	member_release();
}

void BScrollBar::setOrientation(Orientation orientation)
{
	if (member.orientation != orientation)
	{
		member.orientation = orientation;
		emit(Signal_OrientChanged, orientation);
		member.dirty = true;
		this->fresh();
	}
}
Orientation BScrollBar::orientation() const
{
	return member.orientation;
}

bool BScrollBar::setValue(int value)
{
	BRange range(member.range.min(), member.range.max() - member.page);
	value = range.limit(value);
	if (member.value != value)
	{
		member.value = value;
		emit(Signal_ValueChanged, member.value);
		member.dirty = true;
		this->fresh();
		return true;
	}
	return false;
}
int BScrollBar::value() const
{
	return member.value;
}

void BScrollBar::setPage(int page)
{
	page = bLimit(page, 10, member.range.size());
	if (member.page != page)
	{
		member.page = page;
		emit(Signal_PageChanged, member.page);
		member.dirty = true;
		this->fresh();
	}
}
int BScrollBar::page() const
{
	return member.page;
}

void BScrollBar::setMinimum(int minimum)
{
	setRange(BRange(minimum, member.range.max()));
}
int BScrollBar::minimum() const
{
	return member.range.min();
}

void BScrollBar::setMaximum(int maximum)
{
	setRange(BRange(member.range.min(), maximum));
}
int BScrollBar::maximum() const
{
	return member.range.max();
}

void BScrollBar::setRange(int minimum, int maximum)
{
	setRange(BRange(minimum, maximum));
}
void BScrollBar::setRange(const BRange& range)
{
	if(member.range != range)
	{
		member.range = range;
		member.dirty = true;
		emit(Signal_RangeChanged, member.range);
		this->fresh();
	}
}
const BRange& BScrollBar::range() const
{
	return member.range;
}

void BScrollBar::setStep(int step)
{
	if (member.step != step)
	{
		member.step = step;
		emit(Signal_StepChanged, member.step);
	}
}
int BScrollBar::step() const
{
	return member.step;
}
void BScrollBar::moveSteps(int steps)
{
	int value = member.value - member.step * steps;
	this->setValue(value);
}

BSize BScrollBar::sizeHint() const
{
	BSize baseSize = BWidget::sizeHint();
	const BFont* font = this->font();
	if (font)
		baseSize += font->size();
	else
		baseSize += 23;
	return baseSize;
}

void BScrollBar::updateEvent(const BEvent& event)
{

}
void BScrollBar::resizeEvent(const BEvent& event)
{
	member.dirty = true;
}

void BScrollBar::styleEvent(const BEvent& event)
{
	const BStyle* style = this->realStyle();
	if (style)
	{

	}
	member.dirty = false;
}
void BScrollBar::freshEvent(const BEvent& event)
{
	if (1)//member.dirty)
	{
		member.freshHandleRect();
		member.dirty = false;
	}
}
void BScrollBar::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();

	StyleRole colorRole = Color_Handle;
	if(member.pressed)
		colorRole = Color_Handle_Pressed;
	else if(member.hoverHandle)
		colorRole = Color_Handle_Hovered;
	if (painter.setColor(colorRole))
	{
		painter.fillRect(member.handleRect);
		BColor color = painter.color() * 0.5f;
		painter.setColor(color);
		painter.drawRect(member.handleRect);
	}

	painter.drawPerch();
	painter.drawMargin();
	painter.drawBorder();
}

void BScrollBar::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverHandle != false)
	{
		member.hoverHandle = false;
		this->fresh();
	}
	BWidget::mouseEnterEvent(mouseEvent);
}
void BScrollBar::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.hoverHandle != false)
	{
		member.hoverHandle = false;
		this->fresh();
	}
	BWidget::mouseLeaveEvent(mouseEvent);
}

void BScrollBar::mousePressEvent(const BMouseEvent& mouseEvent)
{
	if(mouseEvent.button() == Button_Left)
	{
		member.pressed = true;
		member.pressPos = mouseEvent.pos();
		member.draging = false;
		if (member.handleRect.contain(member.pressPos))
		{
			member.pressValue = member.value;
			member.draging = true;
		}
		if (!member.draging)
		{
			int value = member.value;
			if (member.orientation == Orientation_Horizontal)
			{
				if (member.pressPos.x() < member.handleRect.left())
					value -= member.page;
				if (member.pressPos.x() > member.handleRect.right())
					value += member.page;
			}
			if (member.orientation == Orientation_Vertical)
			{
				if (member.pressPos.y() < member.handleRect.top())
					value -= member.page;
				if (member.pressPos.y() > member.handleRect.bottom())
					value += member.page;
			}
			if (this->setValue(value))
			{
				emit(Signal_Scrolling, value);
			}
		}
		this->fresh();
	}
}
void BScrollBar::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if (mouseEvent.button() == Button_Left)
	{
		member.pressed = false;
		if (member.draging)
		{
			member.draging = false;
			this->emit(Signal_ScrollFinished, member.value);
		}
		this->fresh();
	}
}
void BScrollBar::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if(member.draging)
	{
		int newValue = 0;
		if (member.orientation == Orientation_Horizontal)
		{
			newValue = int((double)(pos.x() - member.pressPos.x()) / width() * member.range.size() + member.pressValue);
		}
		if (member.orientation == Orientation_Vertical)
		{
			newValue = int((double)(pos.y() - member.pressPos.y()) / height() * member.range.size() + member.pressValue);
		}
		if (newValue < member.range.min())
			newValue = member.range.min();
		if (newValue > member.range.max() - member.page)
			newValue = member.range.max() - member.page;
		this->setValue(newValue);
		this->emit(Signal_Scrolling, member.value);
		this->fresh();
	}
	else
	{
		bool hoverHandle = member.handleRect.contain(pos);
		if(member.hoverHandle != hoverHandle)
		{
			member.hoverHandle = hoverHandle;
			this->fresh();
		}
	}
}
bool BScrollBar::mouseWheelEvent(const BMouseEvent& wheelEvent)
{
	int whell = wheelEvent.wheel();
	if (setValue(member.value - whell * member.step))
	{
		this->emit(Signal_Scrolling, member.value);
	}
	return true;
}

